package logger

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"sync"
)

type Level int

const (
	Silent Level = iota + 1
	Error
	Warn
	Info
)

var (
	Ldate         = log.Ldate
	Ltime         = log.Ltime
	Lmicroseconds = log.Lmicroseconds
	Llongfile     = log.Llongfile
	Lshortfile    = log.Lshortfile
	LUTC          = log.LUTC
	Lmsgprefix    = log.Lmsgprefix
	LstdFlags     = log.LstdFlags
)

type ILogger interface {
	SetLevel(Level) ILogger
	SetFlags(int) ILogger
	SetOutput(out io.Writer) ILogger
	Info(...interface{})
	Warn(...interface{})
	Error(...interface{})
	Infof(string, ...interface{})
	Warnf(string, ...interface{})
	Errorf(string, ...interface{})
	Printf(string, ...interface{})
}

func NewLogger(out io.Writer, level Level, flag int) ILogger {
	return &logger{level: level, kit: log.New(out, "\r\n", flag)}
}

type logger struct {
	mux   sync.Mutex
	level Level
	buf   bytes.Buffer
	kit   *log.Logger
}

func (l *logger) SetLevel(level Level) ILogger {
	l.mux.Lock()
	l.level = level
	l.mux.Unlock()
	return l
}

func (l *logger) SetFlags(flag int) ILogger {
	l.kit.SetFlags(flag)
	return l
}

func (l *logger) SetOutput(out io.Writer) ILogger {
	l.kit.SetOutput(out)
	return l
}

func (l *logger) Info(args ...interface{}) {
	l.println(Info, infoStr, args)
}
func (l *logger) Warn(args ...interface{}) {
	l.println(Warn, warnStr, args)
}

func (l *logger) Error(args ...interface{}) {
	l.println(Error, errStr, args)
}

func (l *logger) Infof(format string, args ...interface{}) {
	l.format(Info, infoStr, format, args)
}
func (l *logger) Warnf(format string, args ...interface{}) {
	l.format(Warn, warnStr, format, args)
}
func (l *logger) Errorf(format string, args ...interface{}) {
	l.format(Error, errStr, format, args)
}

func (l *logger) Discard() {
	if l == nil {
		return
	}
	out := l.kit.Writer()
	if w, ok := out.(*stdout); ok {
		if w.File != nil {
			w.File.Close()
			w.File = nil
		}
	}
}

func (l *logger) format(level Level, prefix, format string, args []interface{}) {
	l.mux.Lock()
	if l.level < level {
		l.mux.Unlock()
		return
	}
	l.buf.WriteString(prefix)
	_, err := fmt.Fprintf(&l.buf, format, args...)
	if err == nil {
		l.kit.Output(3, l.buf.String())
	}
	l.buf.Reset()
	l.mux.Unlock()
}

func (l *logger) Printf(format string, args ...interface{}) {
	l.mux.Lock()
	_, err := fmt.Fprintf(&l.buf, format, args...)
	if err == nil {
		flag := l.kit.Flags()
		l.kit.SetFlags(0)
		l.kit.Output(2, l.buf.String())
		l.kit.SetFlags(flag)
	}
	l.buf.Reset()
	l.mux.Unlock()
}

func (l *logger) println(level Level, prefix string, args []interface{}) {
	l.mux.Lock()
	if l.level < level {
		l.mux.Unlock()
		return
	}
	l.buf.WriteString(prefix)
	_, err := fmt.Fprintln(&l.buf, args...)
	if err == nil {
		l.kit.Output(3, l.buf.String())
	}
	l.buf.Reset()
	l.mux.Unlock()
}
